/**
 * 
 * @param {*} container : 作为容器的ul的DOM对象
 * @param {*} rowNum  ：行数，本游戏行数、列数相等
 * @param {*} itemList ：单元格的备选元素列表
 */
function Matrix(container, rowNum, itemList) {
    this.container = container;
    this.rowNum = rowNum;
    this.itemList = itemList; // 单元格的备选元素列表
    this.map = new Array(); // 矩阵地图(2维数组)，所有方块的集合。
    for (var i = 0; i < this.rowNum; i++) {
        var row = [];
        for (var j = 0; j < this.rowNum; j++) {
            row[j] = new Square();
        }
        this.map[i] = row;
    };
    this.vertex1 = null; // 连线转折点1坐标，格式为{x: 0, y: 0}
    this.vertex2 = null; // 连线转折点2坐标，格式为{x: 0, y: 0}
}
/**
 * 判断矩阵是不是空的。如果是空的，则返回true；否则返回false
 */
Matrix.prototype.isEmpty = function () {
    var flag = true; // 默认是空的
    for (var i = 0; i < this.rowNum; i++) {
        for (var j = 0; j < this.rowNum; j++) {
            // 如果矩阵在该点上有成员，并且该成员的item属性的state属性值为true，那么矩阵就不是空的
            if (this.map[i][j] && !this.map[i][j].isEmpty()) {
                flag = false;
                return flag;
            }
        }
    }
    return flag;
}
/**
 * 判断矩阵是不是满的。如果是满的，则返回true；否则返回false
 */
Matrix.prototype.isFull = function () {
    var flag = true; // 默认是满的
    for (var i = 0; i < this.rowNum; i++) {
        for (var j = 0; j < this.rowNum; j++) {
            // 如果矩阵在该点上有成员，并且该成员的item属性的state属性值为true，那么矩阵就不是空的
            if (!this.map[i][j] || this.map[i][j].isEmpty()) {
                flag = false;
                return flag;
            }
        }
    }
    return flag;
}
/**
 * 随机获取一个坐标点，并且保证该坐标点是空的
 */
Matrix.prototype.getEmptyPoint = function () {
    while (true) {
        // 如果矩阵是满的，就返回null，表示没有空白点了
        if (this.isFull()) {
            return null
        }
        var x = randomNum(0, this.rowNum - 1);
        var y = randomNum(0, this.rowNum - 1);
        // 找到空白点，就返回其坐标
        if (!this.map[x][y] || this.map[x][y].isEmpty()) {
            return {x, y}
        }
    }
}

/**
 * 初始化map，随机生成map中各个方块
 * 为了保证矩阵中，出现的每1种方块 数量 都是偶数。
 * 我们每次取2个随机的点，让他们的item都设置为相同的内容。直到矩阵被填满。
 */
Matrix.prototype.init = function () {
    var pointNum = this.rowNum * this.rowNum;
    for (var i = 0; i < pointNum / 2; i++) {
        if (!this.isFull()) { //如果矩阵不是满的
            var point1 = this.getEmptyPoint();
            var point2;
            // 随机取第2个点。如果跟第1个点坐标重复，则重新取点
            do {
                point2 = this.getEmptyPoint();
            } while (point2.x == point1.x && point2.y == point1.y)
            //如果取到了2个点
            if (point1 != null && point2 != null) {
                // 从方块的备选项列表中，随机取出1种
                var index = randomNum(0, this.itemList.length - 1);
                var square1 = new Square();
                square1.init(point1.x, point1.y, this.itemList[index]);
                var square2 = new Square();
                square2.init(point2.x, point2.y, this.itemList[index]);
                this.map[point1.x][point1.y] = square1;
                this.map[point2.x][point2.y] = square2;
            }
        }
    }
}

/**
 * 根据map，在页面上渲染出初始的游戏布局
 */
Matrix.prototype.render = function () {
    var selectArr = []; // 选中的方块
    var matrixInstance = this;
  
    var ulHight = this.container.offsetHeight - this.rowNum;
    var liHight = (ulHight / this.rowNum) + 'px';
    console.log(ulHight)
    for (var i = 0; i < this.rowNum; i++) {
        var li = document.createElement('li');
        li.style.height = liHight;
        for (var j = 0; j < this.rowNum; j++) {
            var div = document.createElement('div');
            var sqr = this.map[i][j];
            var itm = sqr.item;
            div.style.background = sqr.exist ? itm.background : 'transparent';
            div.style.backgroundRepeat = 'no-repeat';
            div.style.backgroundSize = '100% 100%';
            div.setAttribute('x', i);
            div.setAttribute('y', j);
            div.setAttribute('type', itm.type);

            // 添加点击事件
            div.addEventListener('click', function(e) { 
                itm.click(e, selectArr, this, () => {
                    if (selectArr.length === 2) {
                        var square1X = Square.getXAxis(selectArr[0]);
                        var square1Y = Square.getYAxis(selectArr[0]);
                        var square2X = Square.getXAxis(selectArr[1]);
                        var square2Y = Square.getYAxis(selectArr[1]);

                        var square1 = matrixInstance.map[square1X][square1Y];
                        var square2 = matrixInstance.map[square2X][square2Y];

                        matrixInstance.eliminate(square1, square2);
                        selectArr = [];
                    }
                });
             }, false);

            li.appendChild(div);
        }
        ul.appendChild(li)
    }
}

Matrix.prototype.clear = function() {
    while (ul.firstChild) {
        ul.removeChild(ul.lastChild);
    }
}

/**
 * 消除
 * @param {*} square1 第一个方块
 * @param {*} square2 第二个方块
 */
Matrix.prototype.eliminate = function(square1, square2) {
    if (square1['item']['type'] === square2['item']['type']) {
        if (this.lineLink(square1, square2)) {
            square1.remove();
            square2.remove();
            this.clear();
            this.render();
            console.log(this.map)
            return true;
        } 
    }

    return false;
}

/**
 * 判断2个是不是同一个
 */
Matrix.prototype.isSame = function(square1, square2){
    if(square1.x == square2.x && square1.y == square2.y){
        return true;
    }else{
        return false;
    }
}

/**
 * 判断两个点是否可以直线连通
 * 思路：设置一个flag=true 为初始值，即2个点连通的，
 * 然后检测2个点之间的路线上，只要有1个点不是空点，那么这2个点是不能直线连通的。
 */
Matrix.prototype.lineLink = function(square1, square2){
    var small = -1;
    var large = -1;
    var flag = true;
    if(square1.x == square2.x){ // 在一条横线上
        small = square1.y < square2.y ? square1.y : square2.y;
        large = square1.y > square2.y ? square1.y : square2.y;
        if(large - small > 1){ // 如果等于1，那么是相邻元素，一定能连通
            for(var i = small + 1; i < large; i++){
                // 如果所在坐标的square不是null(表示这个坐标上有方块) 并且 该方块的state状态是true，那么认定该坐标点上是有未消除的元素的。那么这条连线是不通的。
                if(this.map[square1.x][i] && this.map[square1.x][i].exist == true){
                    flag = false;
                    break;
                }
            }
        }
    }else{
        if(square1.y == square2.y){ //在一条竖线上
            small = square1.x < square2.x ? square1.x : square2.x;
            large = square1.x > square2.x ? square1.x : square2.x;
            if(large - small > 1){
                for(var i = small + 1; i < large; i++){
                    if(this.map[i][square1.y] && this.map[i][square1.y].exist){
                        flag = false;
                        break;
                    }
                }
            }
        }else{ // 不在一条直线上
            flag = false;
        }
    }  
    return flag; 
}

/**
 * 判断2个点（A点,B点)是不是通过1个直角折线即可连通
 * 思路：1.只有不能直线连通的2个点，才有可能需要通过1个折线连通
 * 2.如果2个点是能通一个折线连通的，那么折线只有2种可能，
 * 并且这2条折线的转折点（2个）与A、B两点，一定是形成一个矩形
 * 3.根据以上2点，我们只需要找出这2个折线的转折点，然后依次判断是否与A点、B点能直线连通即可
 * 如果此转折点既能直线连通A，也能直线连通B，则A、B两点可以经过1个折线连通
 */
Matrix.prototype.oneCornerLink = function (square1, square2) {
    // square1和square2 这2个点一定是对角线关系
    // 求这2个点的折线拐点，就是求矩形的另外2个对角线，
    // square1和square2是什么样的位置关系，矩形的另外2个点求法都是固定的
    var vtx = this.map[square1.x][square2.y]; // 第1个折线拐点
    // 如果此拐点是空的，那么判断它与square1和square2是否能直线连通
    if(vtx.isEmpty()){ //此处调用的是Square.prototype.isEmpty方法
        if(this.lineLink(square1, vtx) && this.lineLink(square2, vtx)){
            this.vertex1 = {x: vtx.x, y: vtx.y};
            return true;
        }
    }else{ // 再尝试判断另外一个拐点是否能连通
        vtx = this.map[square2.x][square1.y];
        if(vtx.isEmpty()){ 
            if(this.lineLink(square1, vtx) && this.lineLink(square2, vtx)){
                this.vertex1 = {x: vtx.x, y: vtx.y};
                return true;
            }
        }
    } 
    // 如果没有找到，无法通过1个拐角的折线连通此2个方块
    this.vertex1 = null;
    return false;
}

/**
 * 判断方块到边界的连通性
 * @param {*} square：方块的坐标。如： {x: 0, y: 0} ，非Square对象
 * @param {*} direction：方向，字符串(top,bottom,left,right)
 */
Matrix.prototype.throughToBorder = function(square, direction){
    direction = direction.toLocaleLowerCase(); // 转换成小写字母
    switch(direction){
        case 'left': //判断该点跟地图左边框是不是连通的
            for(var i = 0; i < square.y; i++){
                // 如果找到任何一个点的state == true，那么就是不连通
                if(this.map[square.x][i].exist){
                    return false;
                }
            }
            break;
        case 'right': //判断该点跟地图右边框是不是连通的
            for(var i = (square.y + 1); i < this.rowNum; i++){
                if(this.map[square.x][i].exist){
                    return false;
                }
            }
            break;
        case 'top': //判断该点跟地图上边框是不是连通的
            for(var i = 0; i < square.x; i++){
                if(this.map[i][square.y].exist){
                    return false;
                }
            }
            break;
        case 'bottom': //判断该点跟地图下边框是不是连通的
            for(var i = (square.x + 1); i < this.rowNum; i++){
                if(this.map[i][square.y].exist){
                    return false;
                }
            }
            break;
    }
    // 如果在switch语句没有执行return语句，说明就是连通的
    return true;
}

/**
 * 2个拐点连通
 */
Matrix.prototype.twoCornerLink = function(square1,square2){
    var x, y;
    // 判断水平向右划线
    // 注意：这里循环判断条件一定得是y <= this.rowNum，而不是<
    for(y = square1.y + 1; y <= this.rowNum; y++){
        if(y == this.rowNum){ //已经到了右边框之外
            // 判断 第2个目标块是否也是与右边框连通的。
            // 如果是连通的，说明这2个块可以连通
            if(this.throughToBorder(square2, 'right')){
                this.vertex1.x = square1.x;
                this.vertex1.y = this.rowNum;
                this.vertex2.x = square2.x;
                this.vertex2.y = this.rowNum;
                return true;
            }
            else{
                break;
            }
        }
        // 如果不是边框之外，就先判断该点是不是未消除的。
        // 如果state是true，说明这条线被堵死了
        if(this.map[square1.x][y].exist){
            break;
        }else{
            var vtx = {x: square1.x, y: y};
            if(this.oneCornerLink(vtx, square2)){
                this.vertex2 = vtx;
                return true;
            }
        }        
    }
    // 水平向左
    for(y = square1.y - 1; y >= -1; y--){
        if(y == -1){ //已到左边框之外
            if(this.throughToBorder(square2, 'left')){
                this.vertex1.x = square1.x;
                this.vertex1.y = -1;
                this.vertex2.x = square2.x;
                this.vertex2.y = -1;
                return true;
            }
            else{
                break;
            }
        }
        if(this.map[square1.x][y].exist){
            break;
        }else{
            var vtx = {x: square1.x, y: y};
            if(this.oneCornerLink(vtx, square2)){
                this.vertex2 = vtx;
                return true;
            }
        }
    }
    // 垂直向上
    for(x = square1.x - 1; x >= -1; x--){
        if(x == -1){ //已到左边框之外
            if(this.throughToBorder(square2, 'top')){
                this.vertex1.x = -1;
                this.vertex1.y = square1.y;
                this.vertex2.x = -1;
                this.vertex2.y = square2.y;
                return true;
            }
            else{
                break;
            }
        }
        if(this.map[x][square1.y].exist){
            break;
        }else{
            var vtx = {x: x, y: square1.y};
            if(this.oneCornerLink(vtx, square2)){
                this.vertex2 = vtx;
                return true;
            }
        }
    }
    // 垂直向下
    for(x = square1.x + 1; x <= this.rowNum; x++){
        if(x == this.rowNum){ //已经到了右边框之外
            // 判断 第2个目标块是否也是与右边框连通的。
            // 如果是连通的，说明这2个块可以连通
            if(this.throughToBorder(square2, 'bottom')){
                this.vertex1.x = this.rowNum;
                this.vertex1.y = square1.y;
                this.vertex2.x = this.rowNum;
                this.vertex2.y = square2.y;
                return true;
            }
            else{
                break;
            }
        }
        // 如果不是边框之外，就先判断该点是不是未消除的。
        // 如果state是true，说明这条线被堵死了
        if(this.map[x][square1.y].exist){
            break;
        }else{
            var vtx = {x: x, y: square1.y};
            if(this.oneCornerLink(vtx, square2)){
                this.vertex2 = vtx;
                return true;
            }
        }        
    }
    // 如果以上4个for语句都没有执行return，说明4个方向都不能连通。
    this.vertex1 = null;
    this.vertex2 = null;
    return false;
    // 以下是简化版的水平方向判断。需要后边测试一下。
    // for(y = -1; y <= this.rowNum; y++){
    //     if(y == -1 || y == this.rowNum){ // 在左、右边框之外
    //         if(this.throughToBorder(square2, 'left')){
    //             this.vertex1.x = square1.x;
    //             this.vertex1.y = y;
    //             this.vertex2.x = square2.x;
    //             this.vertex2.y = y;
    //             return true;
    //         }
    //         else{
    //             break;
    //         }
    //     }
    //     if(this.map[square1.x][y].exist && y != square1.y){
    //         break;
    //     }else{
    //         if(this.map[square1.x][y].exist == false){
    //             var vtx = {x: square1.x, y: y};
    //             if(this.oneCornerLink(vtx, square2)){
    //                 this.vertex2 = vtx;
    //                 return true;
    //             }
    //         }
    //     }
    // }
}